<?php

namespace Lib\Cache;

use Lib\Base\Config;
use Lib\Base\Request;
use Lib\Base\Validate;
use Lib\Service\TGLog;

class RedisAction
{
    private $redis = null;
    private static $redisCon = array();
    private $prefix;
    private $bizConfig;
    private $whiteConfig;

    public function __construct($options = ['groupId' => 0, 'prefix' => 1])
    {
        $this->connRedis($options);
    }

    /**
     *
     * 初始化Redis连接的配置文件
     */
    private function initConfig()
    {
        if ((RUN_ENV == 'ulink' && ENV == 'test') || RUN_ENV == 'internal') {
            if (!defined('REDIS_CONFIG')) {
                recordLog('Redis连接配置信息读取失败！请检查' . APP_PATH . DIRECTORY_SEPARATOR . 'Config' . DIRECTORY_SEPARATOR . RUN_ENV . '.inc.php中常量REDIS_CONFIG是否定义！', __FILE__, __LINE__, LP_ERROR);
                return false;
            }
            $groupNum = count(REDIS_CONFIG);
            for ($i = 0; $i < $groupNum; $i++) {
                $this->bizConfig[$i] = REDIS_CONFIG["REDIS_GROUP_{$i}"];
            }
        } else if (RUN_ENV == 'ulink') {
            $actId = Request::param('iActId');
            if (!Validate::checkNaturalNum($actId)) {
                recordLog('活动ID错误！iActId=' . var_export($actId, true), __FILE__, __LINE__, LP_ERROR);
                return false;
            }

            //解析业务配置信息
            $actConfig = Config::getActConfig($actId);

            if ($actConfig === false) {
                recordLog('活动配置文件读取失败！', __FILE__, __LINE__, LP_ERROR);
                return false;
            }

            $redisConfig = $actConfig['REDIS_CFG'];
            if (empty($redisConfig['host']) || empty($redisConfig['port']) || empty($redisConfig['password'])) {
                recordLog('活动配置文件中缺少Redis连接信息，host=' . var_export($redisConfig['host'], true) . ', port=' . var_export($redisConfig['port'], true) . ', password=' . var_expport($redisConfig['password'], true), __FILE__, __LINE__, LP_ERROR);
            }

            $host = explode('|', $redisConfig['host']);
            $port = explode('|', $redisConfig['port']);
            $password = explode('|', $redisConfig['password']);
            $timeout = explode('|', $redisConfig['timeout']);

            $hostNum = count($host);

            for ($i = 0; $i < $hostNum; $i++) {
                $this->bizConfig[$i]['host'] = $host[$i];
                $this->bizConfig[$i]['port'] = $port[$i];
                $this->bizConfig[$i]['password'] = $password[$i];
                $this->bizConfig[$i]['timeout'] = isset($timeout[$i]) ? $timeout[$i] : 3;
            }

            //解析基础配置信息
            $baseConfig = Config::getBaseConfig();
            if ($baseConfig === false) {
                recordLog('活动基础配置文件读取失败！', __FILE__, __LINE__, LP_ERROR);
                return false;
            }

            $baseRedisConfig = $baseConfig['WHITELIST_REDIS_CFG'];
            if (empty($baseRedisConfig['host']) || empty($baseRedisConfig['port']) || empty($baseRedisConfig['password'])) {
                recordLog('活动基础配置文件中缺少Redis连接信息，host=' . var_export($redisConfig['host'], true) . ', port=' . var_export($redisConfig['port'], true) . ', password=' . var_expport($redisConfig['password'], true), __FILE__, __LINE__, LP_ERROR);
            }
            $this->whiteConfig[0]['host'] = $baseRedisConfig['host'];
            $this->whiteConfig[0]['port'] = $baseRedisConfig['port'];
            $this->whiteConfig[0]['password'] = $baseRedisConfig['password'];
            $this->whiteConfig[0]['timeout'] = isset($baseRedisConfig['timeout']) ? $baseRedisConfig['timeout'] : 3;
        }
        return true;
    }

    /**
     * redis 连接
     */
    private function connRedis($options)
    {
        $config = [];
        $redisLink = '';
        //判断是否要key前缀
        if (isset($options['prefix']) && $options['prefix'] == '0') {
            $this->prefix = false;
        } else {
            $this->prefix = true;
        }

        //支持连接指定的host
        if (isset($options['host']) && isset($options['port']) && isset($options['password'])) {
            $config['host'] = $options['host'];
            $config['port'] = $options['port'];
            $config['password'] = $options['password'];
            $config['timeout'] = isset($options['timeout']) ? $options['timeout'] : 3;
        } else {
            $this->initConfig();
            //如果是ulink实例，则读取基础配置文件中的Redis信息，目前只有活动白名单功能用到
            if (isset($options['instance']) && isset($options['instance']) == 'ulink') {
                $config = $this->whiteConfig[0];
            } else {
                //读取活动配置信息中的Redis连接信息
                $groupId = isset($options['groupId']) ? $options['groupId'] : 0;
                $config = $this->bizConfig[$groupId];
            }
        }

        if (isSwoole()) {
            $requestId = \Swoole\Coroutine::getuid();
            $redisLink = $config['host'] . $config['port'] . $config['password'];
            if (isset(self::$redisCon[$requestId][$redisLink])) {
                $this->redis = self::$redisCon[$requestId][$redisLink];
                return true;
            }
        } else {
            $redisLink = $config['host'] . $config['port'] . $config['password'];
            if (isset(self::$redisCon[$redisLink])) {
                $this->redis = self::$redisCon[$redisLink];
                return true;
            }
        }

        recordLog('init_options:' . var_export($options, true) . ', redis_config:' . var_export($config, true), __FILE__, __LINE__);

        $this->redis = new \Redis();

        $ret = $this->redis->connect($config["host"], $config["port"], $config["timeout"]);

        if (!$ret) {
            $this->redis = null;
            recordLog('连接Redis服务器失败！', __FILE__, __LINE__, LP_ERROR);
            if (RUN_ENV == 'ulink' && ENV != 'test') {
                $apiName = Request::param('route') ? Request::param('route') : (Request::param('c') . '/' . Request::param('a'));
                $data = [
                    ULINK_SAPPID,               //sAppid
                    Request::param('game'),     //sAppKey
                    Request::param('iActId'),   //活动ID
                    Request::param('openid'),   //openid
                    REDIS_CONNECT_ERROR,        //错误码
                    'Redis连接异常，host:' . $config["host"] . ',port:' . $config["port"] . ',timeout:' . $config["timeout"], //错误内容
                    Request::serverParam('SERVER_ADDR'),    //服务器ID
                    Request::getParam('sAmsSerial'),        //流水号
                    date('Y-m-d H:i:s'),        //时间
                    '',
                    '',
                    '',
                    $apiName  //sApiName
                ];
                $tglog = new TGLog(TGLOG_SERVER_INFO);
                $tglog->setTbName('UlinkVitalError');
                $tglog->sendLog($data);
            }
            return false;
        }

        //设置了密码才去认证
        if ($config['password'] != "") {
            $ret = $this->redis->auth($config["password"]);
            if (!$ret) {
                $this->redis = null;
                recordLog('Redis auth认证失败', __FILE__, __LINE__, LP_ERROR);
                return false;
            }
        }

        if (isSwoole()) {
            self::$redisCon[$requestId][$redisLink] = $this->redis;
        } else {
            self::$redisCon[$redisLink] = $this->redis;
        }
        return true;
    }


    /**
     * 调用redis原生方法
     *
     * @param $method
     * @param $argv
     * @return mixed|null
     * @throws \Exception
     */
    public function __call($method, $argv)
    {
        if (empty($this->redis)) {
            recordLog('Redis连接句柄失效！', __FILE__, __LINE__, LP_ERROR);
            return false;
        }

        $mt = strtolower($method);
        if (in_array($mt, ['keys', 'getkeys', 'flushdb', 'flushall', 'echo', 'swapdb'])) {
            recordLog('禁止调用函数:' . $mt, __FILE__, __LINE__, LP_ERROR);
            return false;
        }

        $result = null;

        if (RUN_ENV == 'internal') {
            $actId = Request::param('iActivityId');
            if (!$actId) {
                $actId = Request::param('iAmsActivityId');
            }
            //自写cgi，不走ams配置的，用PaaS实例ID
            if (!$actId && defined('PAAS_INSTANCE_ID') && PAAS_INSTANCE_ID) {
                $actId = PAAS_INSTANCE_ID;
            }
        } else if (RUN_ENV == 'ulink') {
            $actId = Request::param('iActId');
        }

        if (!in_array($mt, ['close']) && $this->prefix) {
            if (in_array($mt, ['mget'])) {
                foreach ($argv[0] as $index => $key) {
                    $argv[0][$index] = RUN_ENV . '_act_' . $actId . '_' . $key;
                }
            } else if (in_array($mt, ['mset', 'msetnx'])) {
                foreach ($argv[0] as $key => $value) {
                    $key = RUN_ENV . '_act_' . $actId . '_' . $key;
                    $argv[0][$key] = $value;
                }
            } else {
                $argv[0] = RUN_ENV . '_act_' . $actId . '_' . $argv[0]; //key自动加上前缀，防止重复
            }
        }

        $result = call_user_func_array(array($this->redis, $method), $argv);

        recordLog("Redis debug: func={$method}, argv: " . var_export($argv, true) . " result: " . var_export($result, true), __FILE__, __LINE__);

        return $result;
    }

    /**
     * 销毁redis链接
     */
    public static function destruct()
    {
        if (isSwoole()) {
            $requestId = \Swoole\Coroutine::getuid();
            if ($requestId > 0 && isset(self::$redisCon[$requestId])) {
                unset(self::$redisCon[$requestId]);
            }
        }
    }
}
